﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Us.Mil.Osd.Acq.Damir.Test
{
    
    /// <summary>
    /// Represents a query, where its Expression property is like an execution plan, and execution is deferred until
	/// the call to IQueryProvider.Execute.	
	/// 
    /// </summary>
    public class Queryable<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable
    {
        QueryProvider provider;
        Expression expression;
		long id;


		public Queryable(QueryProvider provider)
			: this(provider: provider, id: 0) {
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="provider">QueryProvider</param>
		/// <param name="id">A semantic identifier for the query, used for caching.</param>
		public Queryable(QueryProvider provider, long id) {
			this.expression = Expression.Constant(this);
			this.provider = provider;
			this.id = id;
		}
		public Queryable(QueryProvider provider, 
			long id, Expression expression)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }            
			if (!(typeof(IQueryable<T>).IsAssignableFrom(expression.Type) || typeof(IEnumerable<T>).IsAssignableFrom(expression.Type)))
            {
                throw new ArgumentOutOfRangeException("expression");
            }
            this.provider = provider;
            this.expression = expression ?? Expression.Constant(this);
			this.id = id;
        }

		/// <summary>
		/// To identify this query semantically, and not by object instance (i.e. not GetHashCode). 
		/// The ID can be re-used across different instances of the same semantic query so they can use the cached result from CachingSqlRequestProcessor.
		/// </summary>
		public long ID {
			get { return id; } 
		}
		
        public Expression Expression
        {
            get { return this.expression; }
        }

        public Type ElementType
        {
            get { return typeof(T); }
        }

        public IQueryProvider Provider
        {
            get { return this.provider; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            //return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator();
			return ((IEnumerable<T>)this.provider.Execute(this.expression, this.ID)).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            //return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator();
			return ((IEnumerable)this.provider.Execute(this.expression, this.ID)).GetEnumerator();
        }
		
        public override string ToString()
        {
			return this.TSQL;
        }
		
		public string TSQL {
			get { return provider.GetQueryText(this.expression); }
		}
        

		
	}
}
